package com.wfh.easychat.service.impl;

import cn.hutool.crypto.digest.BCrypt;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import cn.hutool.jwt.JWTUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wfh.easychat.common.ErrorCode;
import com.wfh.easychat.constant.RedisConstant;
import com.wfh.easychat.constant.UserConstant;
import com.wfh.easychat.exception.BusinessException;
import com.wfh.easychat.mapper.UserInfoMapper;
import com.wfh.easychat.model.entity.UserInfo;
import com.wfh.easychat.model.entity.UserInfoBeauty;
import com.wfh.easychat.model.enums.BeautyAccountStatusEnum;
import com.wfh.easychat.model.enums.UserStatusEnum;
import com.wfh.easychat.model.vo.UserInfoVo;
import com.wfh.easychat.service.UserInfoBeautyService;
import com.wfh.easychat.service.UserInfoService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
* @author fenghuanwang
* @description 针对表【user_info(用户信息)】的数据库操作Service实现
* @createDate 2025-09-24 14:28:31
*/
@Service
@Slf4j
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo>
    implements UserInfoService{

    @Resource
    @Lazy
    private UserInfoBeautyService userInfoBeautyService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * tokenkey
     */
    private static final String TOKEN_KEY = "easychat";

    /**
     * 获取当前登录用户
     * @param request
     * @return
     */
    @Override
    public UserInfo getLoginUser(HttpServletRequest request) {
        Object attribute = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
        return (UserInfo) attribute;
    }

    /**
     * 用户注册
     *
     * @param email
     * @param passwrod
     * @param nickName
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserInfoVo userregister(String email, String passwrod, String nickName) {
        // 先对密码进行加密
        String bcryptPwd = DigestUtil.bcrypt(passwrod);
        // 查询数据库中是否存在该用户
        LambdaQueryWrapper<UserInfoBeauty> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserInfoBeauty::getEmail, email);
        UserInfoBeauty userInfoBeauty = userInfoBeautyService.getBaseMapper().selectOne(queryWrapper);
        if (userInfoBeauty == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "靓号不存在");
        }
        if (BeautyAccountStatusEnum.USED.getValue().equals(userInfoBeauty.getStatus())) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "靓号已经使用");
        }
        // 账号存在且没有被使用，直接插入到数据库
        UserInfo userInfo = new UserInfo();
        userInfo.setPassword(bcryptPwd);
        userInfo.setEmail(email);
        userInfo.setNickName(nickName);
        userInfo.setStatus(UserStatusEnum.NORMAL.getValue());
        userInfo.setLastLoginTime(new Date());
        userInfo.setLastOffTime(new Date());
        boolean save = this.save(userInfo);
        if (!save) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败");
        }
        Long userInfoId = userInfo.getId();
        userInfoBeauty.setUserId(userInfoId);
        userInfoBeauty.setStatus(BeautyAccountStatusEnum.USED.getValue());
        // 更新靓号状态
        boolean b = userInfoBeautyService.updateById(userInfoBeauty);
        if (!b) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败");
        }
        return UserInfo.objToVo(userInfo);
    }

    /**
     * 用户登录
     * @param email
     * @param passwrod
     * @return
     */
    @Override
    public Map<Object, Object> login(String email, String passwrod, HttpServletRequest request) {
        // 查询是否存在该账号
        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
        UserInfo userInfo = this.getOne(queryWrapper.eq(UserInfo::getEmail, email));
        if (userInfo == null){
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "账号不存在");
        }
        String bcryptPwd = userInfo.getPassword();
        boolean checkpw = BCrypt.checkpw(passwrod, bcryptPwd);
        if (!checkpw){
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "密码错误");
        }
        sameDeviceCheck(request, userInfo);
        // 记录登录状态
        request.getSession().setAttribute(UserConstant.USER_LOGIN_STATE, userInfo);
        // 生成token
        String token = genToken(TOKEN_KEY, userInfo);
        HashMap<Object, Object> res = new HashMap<>();
        res.put("userInfoVo", UserInfo.objToVo(userInfo));
        res.put("token", token);
        return res;
    }

    /**
     * 管理员登录
     * @param email
     * @param password
     * @param request
     * @return
     */
    @Override
    public UserInfo adminLogin(String email, String password, HttpServletRequest request) {
        // 查询数据库是否存在该用户
        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserInfo::getEmail, email);
        UserInfo userInfo = this.getOne(queryWrapper);
        if (userInfo == null){
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "用户不存在");
        }
        // 验证密码是否正确
        String userInfoPassword = userInfo.getPassword();
        boolean checkpw = BCrypt.checkpw(password, userInfoPassword);
        if (!checkpw){
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "密码错误");
        }
        // 同端登录冲突检测
        sameDeviceCheck(request, userInfo);
        // 数量不是0，说明账号存在，登录成功, 设置session
        request.getSession().setAttribute(UserConstant.USER_LOGIN_STATE, userInfo);
        return userInfo;
    }

    /**
     * 同端登录检测
     * @param request
     * @param userInfo
     */
    private void sameDeviceCheck(HttpServletRequest request, UserInfo userInfo) {
        String sessionId = request.getSession().getId();
        // 获取登录设备的id，实现同端登录冲突检测
        UserAgent parse = UserAgentUtil.parse(request.getHeader("User-Agent"));
        String name = parse.getPlatform().getName();
        String deviceKey =RedisConstant.LOGIN_DEVICE_KEY + userInfo.getId() + ":" + name;
        Boolean ifAbsent = stringRedisTemplate.opsForValue().setIfAbsent(deviceKey, sessionId, 1800, TimeUnit.SECONDS);
        if (!ifAbsent){
            // 已经存在了, 删除对应的session
            String oldSessionId = stringRedisTemplate.opsForValue().get(deviceKey);
            boolean delete = true;
            if(oldSessionId != null && !oldSessionId.equals(sessionId)){
                delete = stringRedisTemplate.opsForValue().getOperations().delete("spring:session:sessions:" + oldSessionId);
            }
            // 更新为当前的sessionid
            stringRedisTemplate.opsForValue().set(deviceKey, sessionId, 1800, TimeUnit.SECONDS);
            if (delete){
                log.info("用户 {} 在 {} 设备上登录，踢掉旧 session [{}]", userInfo.getId(), name, oldSessionId);
            }
        }
    }

    /**
     * 生成token
     *
     * @param secretKey
     * @return
     */
    private String genToken(String secretKey, UserInfo userInfo) {
        byte[] bytes = secretKey.getBytes();
        Map<String, Object> pyload = new HashMap<>();
        pyload.put("user", userInfo);
        return JWTUtil.createToken(pyload, bytes);
    }
}




